<template>
    <div class="overflow-y-auto" ref="scrollRef" key="scroll">
        <div ref="scrollContentRef" key="scroll-content">
            <template v-for="(msg, idx) in data.messages">
                <div class="timestamp text-center mt-4 mb-2"
                     v-if="msg.timeMillis - (data.messages[idx - 1]?.timeMillis ?? 0) > 30 * 60 * 1000">
                    <span class="content">{{ msg.createTime }}</span>
                </div>
                <AnyMessage :msg="msg"></AnyMessage>
            </template>
            <div class="h-2" key="bottom-anchor" ref="bottomAnchorRef"></div>
        </div>
    </div>
</template>

<script setup lang="ts">
import { Loader } from 'src/framework/network/Loader';
import { onMounted, reactive, ref, watch, nextTick, onActivated, onDeactivated } from 'vue';
import * as api from './api'
import AnyMessage from './components/AnyMessage.vue';
const props = defineProps<{
    openId?: string,
    appId?: string
}>()

const mounting = Promise.manual<void>()
onMounted(mounting.resolve)

const loader = reactive(new Loader(api.fetchMessages, () => props))

const data = reactive({
    messages: [] as api.AnyMessage[]
})

let canceller: ReturnType<typeof Promise.setInterval> | undefined;
let canceller2: ReturnType<typeof scrollToBottomUntilUserInteract> | undefined;

onActivated(() => {
    if (props.openId) {
        reload()
    }
})

watch(() => props.openId, (val, old) => {
    if (val && val !== old) {
        reload()
    }
})


onDeactivated(() => {
    canceller?.cancel()
    canceller2?.cancel()
})


async function reload() {
    await mounting
    data.messages = []
    data.messages = await loader.load()
    canceller?.cancel()
    canceller2?.cancel()
    canceller2 = scrollToBottomUntilUserInteract()
    canceller = Promise.setInterval(refresh, 5000)
}

/* 由于图片视频等内容加载前后高度不同，简单的滚动到底部可能会由于后续加载过程中滚动容器的高度增加而不再停留在底部
遂提供一个函数，持续监听滚动容器的高度，在其发生改变时继续停留在底部
同时为了不影响来自用户的滚动操作，在交互事件发生时自动取消这一行为 */
const scrollRef = ref<HTMLDivElement>()
const scrollContentRef = ref<HTMLDivElement>()
function scrollToBottomUntilUserInteract() {
    const scroll = scrollRef.value!
    const cancel = () => resizeObserver.disconnect()
    scroll.onwheel = cancel
    scroll.ontouchstart = cancel
    scroll.onmousedown = cancel
    scroll.scrollTop = scroll.scrollHeight
    const resizeObserver = new ResizeObserver(() => {
        console.log(scroll.scrollHeight)
        scroll.scrollTop = scroll.scrollHeight
    })
    resizeObserver.observe(scrollContentRef.value!)

    return { cancel }
}

/* 监听聊天记录是否触底，并维护一个状态，以指示在聊天记录触底的情况下新消息应继续保持触底 */
let observer: IntersectionObserver | undefined
const bottomAnchorRef = ref<HTMLDivElement>()
let shouldStayOnBottom = true
onMounted(() => {
    observer = new IntersectionObserver((entries) => {
        shouldStayOnBottom = entries[0].isIntersecting
    }, { root: scrollRef.value! })
})
onActivated(() => observer!.observe(bottomAnchorRef.value!))
onDeactivated(() => observer!.unobserve(bottomAnchorRef.value!))

async function refresh() {
    canceller2?.cancel()
    data.messages = data.messages.concat(await loader.load({ timeInMilleSeconds: data.messages[data.messages.length - 1].timeMillis }))
    if (shouldStayOnBottom) {
        nextTick(scrollToBottom)
    }
}

function scrollToBottom(animated: boolean = true) {
    bottomAnchorRef.value!.scrollIntoView({ behavior: animated ? 'smooth' : 'auto' })
}

defineExpose({
    reload, refresh, scrollToBottom
})

</script>

<style scoped>
:deep(.page-content) {
    background: #F8F8F8;
}

:deep(.card .content .body) {
    @apply px-0 py-2;
}

.timestamp .content {
    display: inline-block;
    background: #DFDFDF;
    border-radius: 4px;
    font-weight: 400;
    font-size: 12px;
    line-height: 22px;
    font-feature-settings: 'tnum' on, 'lnum' on;
    color: #FFFFFF;
    padding: 2px 8px;
}
</style>